
import java.util.ArrayList;

public class Maze {
	private static ArrayList<ArrayList<Cell>> m = new ArrayList<ArrayList<Cell>>(); //Matrix of Cells
	private static int width; //width in Cells
	private static int height; //height in Cells

	//constructor
	
	public Maze(int width, int height) {
		Maze.width = width;
		Maze.height = height;
		initMaze();
		setNeighbors();
	}

	//build the matrix, initializing all Cells
	private void initMaze() {
		for (int y = 0; y < height; y++) {
			m.add(new ArrayList<Cell>());
			for (int x = 0; x < width; x++) {
				m.get(y).add(new Cell(x, y));
			}
		}
	}
	
	public static int getWidth() {

		return width;
	}

	public static int getHeight() {

		return height;
	}
	


	//set the neighbors of each Cell in the maze
	private void setNeighbors() {
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				ArrayList<Cell> n = new ArrayList<Cell>();
				Cell c = getCell(x, y);
				if (y != 0) {
					n.add(getCell(x, y - 1));
				}
				if (y != height - 1) {
					n.add(getCell(x, y + 1));
				}
				if (x != 0) {
					n.add(getCell(x - 1, y));
				}
				if (x != width - 1) {
					n.add(getCell(x + 1, y));
				}
				c.setNeighbors(n);
			}
		}
	}

	//get Cell at given coordinates
	public Cell getCell(int x, int y) {
		return m.get(y).get(x);
	}
	

	

	//Draw every Cell in the maze
	public void drawMaze(Sketch s) {
		for (ArrayList<Cell> a : m) {
			for (Cell c : a) {
				c.drawCell(s);
			}
		}
	}
	
	//highlight maze
	public void markMaze(Sketch s){
		for(ArrayList<Cell> a : m){
			for (Cell c : a){
				c.markCell(s);
			}
		}
	}
	
	//highlight maze
		public void markEnd(Sketch s){
			for(ArrayList<Cell> a : m){
				for (Cell c : a){
					c.markEnd(s);
				}
			}
		}
	
	//reset visits
	public void resetVisits(){
		for (ArrayList<Cell> a : m) {
			for (Cell c : a) {
				c.resetVisit();
			}
		}
	}
	
	
	public static double testRoute(int[] path, int[][]memory, int[] chromozome ){
		int posX = 0;
		int posY = 0;
		for (int dir = 0; dir < path.length; ++dir) {

			//int nextDir = path[dir];

	/*restart:*/	switch(path[dir]) {
			case 1: // north
				
				// check if within bounds and that we can move
				if (posY - 1 < 0 || m.get(posY-1).get(posX).southWall || m.get(posY-1).get(posX).isEndtoPath()) {
					path[dir] = m.get(posY).get(posX).pickGeneNeighbor();
					chromozome[dir]=path[dir]; //repair gene
					
					break;//  restart;
					}
				 else {
					posY -= 1;
					break;
				}

			case 2: // east
				if(!(posX+1 >= width) && m.get(posY).get(posX+1).isEnd() && !m.get(posY).get(posX+1).westWall){
					System.out.println("Solution found!");
					posX+=1;
					break;
				}
				// check within bounds and that we can move
				if (posX+1 >= width || m.get(posY).get(posX+1).westWall || m.get(posY).get(posX+1).isEndtoPath() ) {

					path[dir] = m.get(posY).get(posX).pickGeneNeighbor();
					chromozome[dir]=path[dir]; //repair gene 
					
					break;//  restart;
				} else {
					posX += 1;
					break;
				}
				
			case 3: // south
				if(!(posY+1 >= height) && m.get(posY+1).get(posX).isEnd() && !m.get(posY+1).get(posX).northWall ){
					System.out.println("Solution found!");
					posY+=1;
					break;
				}
				// check within bounds and that we can move
				if (posY+1 >= height || m.get(posY+1).get(posX).northWall || m.get(posY+1).get(posX).isEndtoPath()) {

					path[dir] = m.get(posY).get(posX).pickGeneNeighbor();
					chromozome[dir]=path[dir]; //repair gene 
					
					break;//  restart;
					
				} else {
					posY += 1;
					break;
				}
			case 4: // west
				
				// check within bounds and that we can move
				if (posX-1 < 0 ||m.get(posY).get(posX-1).eastWall || m.get(posY).get(posX-1).isEndtoPath()) {
					path[dir] = m.get(posY).get(posX).pickGeneNeighbor();
					chromozome[dir]=path[dir]; //repair gene 
					
					break;//  restart;
				} else {
					posX -= 1;
					break;
				}

			} // end switch


			// mark the route in memory
			memory[posX][posY] = 1;
			double	dx = Math.pow(((double)width -posX), 2.00);
			double	dy = Math.pow(((double)height -posY), 2.00);
			double fitness = Math.sqrt(dx+dy);
			if(fitness == 1.4142135623730951) return fitness;
		} // next direction
		// assign a fitness score which is proportional to the distance from the exit
		//?penalise for leading close to dead-end
		double	dx = Math.pow((width -posX), 2.00);
		double	dy = Math.pow((height -posY), 2.00);
		return Math.sqrt(dx+dy); //distance from goal
	}
}
